/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package uk.ac.cam.can.algorithm;

import uk.ac.cam.can.Data;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.util.*;

/**
 *
 * @author Thomas
 */
public class VAnon  extends LDiverse{
    private int quasisIdPos=0;
    private int eventsIdPos=0;
    private Data events;
    private int eoSize;
    
    public VAnon(Data patients, Data events, int k, double d, int l, int c){
        super(patients, k, d, l, c);
        this.events=events;
        events.load();
        events.load();
        this.eoSize=events.size();
    }
    
    @Override
    protected PosGen[][] getAttributesGeneralisationHierarchy(){
        PosGen[][] p = new PosGen[data.nbAttributes()+events.nbAttributes()][];
        for(int i = 0; i < data.nbAttributes(); i++){
            p[i] = new PosGen[data.getMaxGeneralisation(i)];
            for(int j = 0; j < p[i].length; j++){
                p[i][j]=new PosGen(i, j);
            }
        }
        for(int i=0; i < events.nbAttributes(); i++){
            p[i+data.nbAttributes()] = new PosGen[events.getMaxGeneralisation(i)];
            for(int j = 0; j < p[i+data.nbAttributes()].length; j++){
                p[i+data.nbAttributes()][j]=new PosGen(i+data.nbAttributes(), j);
            }
        }
        return p;
    }
    
    @Override
    protected void mark(PosGen[] cmb){
        PosGen[][] hierarchy = new PosGen[cmb.length][];
        for(int i = 0; i < hierarchy.length; i++){
            int nb;
            if(cmb[i].position() < data.nbAttributes()){
                nb = data.getMaxGeneralisation(cmb[i].position()) - cmb[i].generalisation();
                hierarchy[i]=new PosGen[nb];
            }else{
                nb = events.getMaxGeneralisation(cmb[i].position()-data.nbAttributes()) - cmb[i].generalisation();
                hierarchy[i]=new PosGen[nb];
            }
            for(int j = 0; j < nb; j++){
                hierarchy[i][j]= new PosGen(cmb[i].position(), cmb[i].generalisation()+j);
            }
        }
        
        List<PosGen[]> output = new ArrayList<PosGen[]>();
        permutation(hierarchy, 0, new ArrayList<PosGen>(), output);
        for(PosGen[] cmbs : output){
            String tmp="";
            for(PosGen pg : cmbs)
                tmp+=pg.toString();
            pruneResults.put(tmp, true);
        }
    }
    
    public void deleteEvents(String id){
        List<String[]> toBeDeleted = new ArrayList<String[]>();
        for(String[] record : events.getRecords()){
            if(record[eventsIdPos].equals(id)){
                toBeDeleted.add(record);
            }
        }
        for(String[] record : toBeDeleted){
            events.remove(record);
        }
    }
    
    @Override
    protected boolean testDeletion(){
        int deleted = eoSize-events.size();
        if(deleted > (int)(eoSize*d))
            return false;
        return super.testDeletion();
    }
    
    public String getEvents(PosGen[] combination, String id){
        List result = new ArrayList<String>();
        for(String[] record : events.getRecords()){
            if(record[eventsIdPos].equals(id)){
                String tmp="";
                for(PosGen pg : combination){
                    if(pg.position()<data.nbAttributes())
                        continue;
                    if(pg.position()-data.nbAttributes()!=eventsIdPos){
                        tmp+=record[pg.position()-data.nbAttributes()];
                    }
                }
                result.add(tmp);
            }
            
        }
        // if we have a signle result send it
        if(result.size()==1)
            return (String)result.get(0);
        
        // we need to sort it and remove duplicate
        Object[]  arr = result.toArray();
        Arrays.sort(arr);
        for(int i=0; i < arr.length-1; i++){
            if(arr[i].equals(arr[i+1]))
                result.remove(arr[i]);
        }
        
        //build the final result
        String tmp="";
        for(Object r : result){
            tmp+=(String)r;
        }
        return tmp;
    }
    
    @Override
    public boolean isAnonymous(PosGen[] combination){
        data.load();
        events.load();
        //dump(combination);
        List<PosGen> dataCmb = new ArrayList<PosGen>();
        List<PosGen> eventCmb = new ArrayList<PosGen>();
        for(PosGen pg : combination){
            if(pg.position() < data.nbAttributes())
                dataCmb.add(pg);
            else
                eventCmb.add(pg);
        }        
        PosGen[] dataCombination = new PosGen[dataCmb.size()];
        for(int i = 0; i < dataCmb.size(); i++){
            dataCombination[i] = dataCmb.get(i);
        }
        
        PosGen[] eventsCombination = new PosGen[combination.length-dataCmb.size()];
        for(int i = 0; i < eventCmb.size(); i++){
            eventsCombination[i] = eventCmb.get(i);
        }
        
        for(PosGen pg : dataCombination){
            data.generalise(pg.position(), pg.generalisation());
        }
        
        for(PosGen pg : eventsCombination){
            events.generalise(pg.position()-data.nbAttributes(), pg.generalisation());
        }
        
        
        // build the q groups
        Map<String, List<String[]>> dataQGroup = new HashMap<String, List<String[]>>();
        for(String[] record : data.getRecords()){
            String qi="";
            // extract quasi-identifiers from the patients table
            for(PosGen pg : dataCombination){
                if(data.getType(pg.position())==Data.TYPE_QUASI_IDENTIFIER)
                    qi+=record[pg.position()];
            }
            // extract the quais-identifiers from the events table
            qi+=this.getEvents(eventsCombination, record[quasisIdPos]);
                
            if(qi.equals(""))
                return true;
            if(dataQGroup.containsKey(qi)){
                dataQGroup.get(qi).add(record);
            }else{
                //System.out.println(qi);
                dataQGroup.put(qi, new ArrayList<String[]>());
                dataQGroup.get(qi).add(record);
            }            
        }
        
        // verify the qgroups meet the criteria
        for(List<String[]> qgroup : dataQGroup.values()){
            if(!criteria(dataCombination, qgroup)){
                for(String[] record : qgroup)
                    this.deleteEvents(record[quasisIdPos]);
                data.removeAll(qgroup);
            }
        }
        return testDeletion();
    }
    
    @Override
    protected int getMaxCost(){
        return data.getMaxCost()+events.getMaxCost();
    }
    
    @Override
    protected int computeCost(PosGen[] cmb){        
        List<PosGen> dataCmb = new ArrayList<PosGen>();
        List<PosGen> eventCmb = new ArrayList<PosGen>();
        for(PosGen pg : cmb){
            if(pg.position() < data.nbAttributes())
                dataCmb.add(pg);
            else
                eventCmb.add(pg);
        }        
        PosGen[] dataCombination = new PosGen[dataCmb.size()];
        for(int i = 0; i < dataCmb.size(); i++){
            dataCombination[i] = dataCmb.get(i);
        }
        
        PosGen[] eventsCombination = new PosGen[cmb.length-dataCmb.size()];
        for(int i = 0; i < eventCmb.size(); i++){
            eventsCombination[i] = eventCmb.get(i);
        }
        
        int c=0;        
        for(PosGen pg : dataCombination){
            c += pg.generalisation() * data.getCost(pg.position());
        }
        
        for(PosGen pg : eventsCombination){
            c += pg.generalisation() * events.getCost(pg.position()-data.nbAttributes());
        }
        return c;
    }
    
    @Override
    public void saveBest(String fileName) throws Exception{
        super.saveBest(fileName);
        File f = new File("events-"+fileName);
        f.delete();
        BufferedWriter buff = new BufferedWriter(new FileWriter("events-"+fileName));
        for(String[] r : data.getRecords()){
            Object[] evts = getEventsToSave(r[0]);
            for(Object evt : evts){
                buff.write((String)evt);
                buff.newLine();
            }
        }
        buff.flush();
        buff.close();
    }
    
    public Object[] getEventsToSave(String id){
            List<String> result = new ArrayList<String>();
            for(String[] record : events.getRecords()){
                if(record[eventsIdPos].equals(id)){
                    String tmp="";
                    for(int i=0; i < record.length; i++){
                        if(i!=eventsIdPos)
                            tmp+=record[i]+";";
                    }
                    result.add(id+";"+tmp);
                }

            }
            // if we have a signle result send it
            if(result.size()==1){
                String[] t= new String[1];
                t[0]=result.get(0);
            }

            // we need to sort it and remove duplicate
            Object[]  arr = result.toArray();
            Arrays.sort(arr);
            for(int i=0; i < arr.length-1; i++){
                if(arr[i].equals(arr[i+1]))
                    result.remove((String)arr[i]);
            }
            return result.toArray();
        }
}
